package modbus;

import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
import com.intelligt.modbus.jlibmodbus.exception.ModbusNumberException;
import com.intelligt.modbus.jlibmodbus.exception.ModbusProtocolException;
import com.intelligt.modbus.jlibmodbus.master.ModbusMaster;
import com.intelligt.modbus.jlibmodbus.msg.ModbusRequestBuilder;
import com.intelligt.modbus.jlibmodbus.msg.base.ModbusRequest;
import com.intelligt.modbus.jlibmodbus.msg.response.*;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import modbus.protocol.HoldingRegistersMessage;

@Slf4j
public class ModbusMasterMixin {

    protected final ModbusMaster modbusMaster;

    public ModbusMasterMixin(ModbusMaster modbusMaster) {
        this.modbusMaster = modbusMaster;
    }

    private void open() throws ModbusIOException {
        // since 1.2.8
        if (!modbusMaster.isConnected()) {
            modbusMaster.connect();
        }
    }

    final public ReadHoldingRegistersResponse readHoldingRegisters(int slaveId, int offset, int quantity) throws ModbusNumberException, ModbusIOException, ModbusProtocolException {
        open();
        ModbusRequest request = ModbusRequestBuilder.getInstance().buildReadHoldingRegisters(slaveId, offset, quantity);
        ReadHoldingRegistersResponse modbusResponse = (ReadHoldingRegistersResponse) modbusMaster.processRequest(request);
        return modbusResponse;
    }

    public int[] readHoldingRegistersToInt(int slaveId, int offset, int quantity) throws ModbusProtocolException, ModbusNumberException, ModbusIOException {
        ReadHoldingRegistersResponse response = readHoldingRegisters(slaveId, offset, quantity);
        return response.getHoldingRegisters().getRegisters();
    }

    final public byte[] readHoldingRegistersToByte(int slaveId, int offset, int quantity) throws ModbusProtocolException, ModbusNumberException, ModbusIOException {
        ReadHoldingRegistersResponse response = readHoldingRegisters(slaveId, offset, quantity);
        return response.getBytes();
    }

    /**
     *
     * @param slaveId
     * @param offset
     * @param quantity
     * @param format 数据格式不能为空
     * @return
     * @throws ModbusNumberException
     * @throws ModbusIOException
     * @throws ModbusProtocolException
     */
    public int[] readHoldingRegistersToInt(int slaveId, int offset, int quantity, @NonNull String format) throws ModbusNumberException, ModbusIOException, ModbusProtocolException {
        byte[] responseBytes = readHoldingRegistersToByte(slaveId, offset, quantity);
        HoldingRegistersMessage message = new HoldingRegistersMessage();
        int[] values = message.parseHoldingRegistersToInt(format, responseBytes);
        return values;
    }

    public float[] readHoldingRegistersToFloat(int slaveId, int offset, int quantity, @NonNull String format) throws ModbusNumberException, ModbusIOException, ModbusProtocolException {
        byte[] responseBytes = readHoldingRegistersToByte(slaveId, offset, quantity);
        HoldingRegistersMessage message = new HoldingRegistersMessage();
        float[] values = message.parseHoldingRegistersToFloat(format, responseBytes);
        return values;
    }

    public double[] readHoldingRegistersToDouble(int slaveId, int offset, int quantity, @NonNull String format) throws ModbusNumberException, ModbusIOException, ModbusProtocolException {
        byte[] responseBytes = readHoldingRegistersToByte(slaveId, offset, quantity);
        HoldingRegistersMessage message = new HoldingRegistersMessage();
        double[] values = message.parseHoldingRegistersToDouble(format, responseBytes);
        return values;
    }

    public Object[] readHoldingRegistersToObject(int slaveId, int offset, int quantity, @NonNull String format) throws ModbusNumberException, ModbusIOException, ModbusProtocolException {
        byte[] responseBytes = readHoldingRegistersToByte(slaveId, offset, quantity);
        Object[] values = HoldingRegistersMessage.build().parseHoldingRegistersToObject(format, responseBytes);
        return values;
    }

    final public ReadCoilsResponse readCoilsResponse(int slaveId, int offset, int quantity) throws ModbusNumberException, ModbusIOException, ModbusProtocolException {
        open();
        ModbusRequest request = ModbusRequestBuilder.getInstance().buildReadCoils(slaveId, offset, quantity);
        ReadCoilsResponse modbusResponse = (ReadCoilsResponse) modbusMaster.processRequest(request);
        return modbusResponse;
    }

    final public boolean[] readCoils(int slaveId, int offset, int quantity) throws ModbusNumberException, ModbusIOException, ModbusProtocolException {
        ReadCoilsResponse response = readCoilsResponse(slaveId, offset, quantity);
        boolean[] data = response.getCoils();
        return data;
    }

    final public Object[] readCoilsToObject(int slaveId, int offset, int quantity, @NonNull String format) throws ModbusNumberException, ModbusIOException, ModbusProtocolException {
        ReadCoilsResponse response = readCoilsResponse(slaveId, offset, quantity);
        byte[] data = response.getBytes();
        Object[] values = HoldingRegistersMessage.build().parseHoldingRegistersToObject(format, data);
        return values;
    }

    final public ReadExceptionStatusResponse readExceptionStatusResponse(int slaveId, int offset, int quantity) throws ModbusNumberException, ModbusIOException, ModbusProtocolException {
        open();
        ModbusRequest request = ModbusRequestBuilder.getInstance().buildReadCoils(slaveId, offset, quantity);
        ReadExceptionStatusResponse modbusResponse = (ReadExceptionStatusResponse) modbusMaster.processRequest(request);
        return modbusResponse;
    }

    final public int readExceptionStatus(int slaveId, int offset, int quantity) throws ModbusNumberException, ModbusIOException, ModbusProtocolException {
        ReadExceptionStatusResponse response = readExceptionStatusResponse(slaveId, offset, quantity);
        return response.getExceptionStatus();
    }

    final public byte[] readExceptionStatusResponseToByte(int slaveId, int offset, int quantity) throws ModbusNumberException, ModbusIOException, ModbusProtocolException {
        ReadCoilsResponse response = readCoilsResponse(slaveId, offset, quantity);
        return response.getBytes();
    }

    final public Object[] readExceptionStatusResponseToObject(int slaveId, int offset, int quantity, @NonNull String format) throws ModbusNumberException, ModbusIOException, ModbusProtocolException {
        byte[] data = readExceptionStatusResponseToByte(slaveId, offset, quantity);
        Object[] values = HoldingRegistersMessage.build().parseHoldingRegistersToObject(format, data);
        return values;
    }

    final public ReadInputRegistersResponse readInputRegistersResponse(int slaveId, int offset, int quantity) throws ModbusNumberException, ModbusIOException, ModbusProtocolException {
        open();
        ModbusRequest request = ModbusRequestBuilder.getInstance().buildReadInputRegisters(slaveId, offset, quantity);
        ReadInputRegistersResponse modbusResponse = (ReadInputRegistersResponse) modbusMaster.processRequest(request);
        return modbusResponse;
    }

    final public byte[] readInputRegistersToByte(int slaveId, int offset, int quantity) throws ModbusNumberException, ModbusIOException, ModbusProtocolException {
        return readInputRegistersResponse(slaveId, offset, quantity).getBytes();
    }

    final public int[] readInputRegisters(int slaveId, int offset, int quantity) throws ModbusNumberException, ModbusIOException, ModbusProtocolException {
        ReadInputRegistersResponse response = readInputRegistersResponse(slaveId, offset, quantity);
        return response.getRegisters();
    }

    final public Object[] readInputRegistersToObject(int slaveId, int offset, int quantity, @NonNull String format) throws ModbusProtocolException, ModbusNumberException, ModbusIOException {
        byte[] data = readInputRegistersToByte(slaveId, offset, quantity);
        Object[] values = HoldingRegistersMessage.build().parseHoldingRegistersToObject(format, data);
        return values;
    }

    final public ReadWriteMultipleRegistersResponse readWriteMultipleRegistersResponse(int serverAddress, int readAddress, int readQuantity, int writeAddress, int[] registers) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException {
        ModbusRequest request = ModbusRequestBuilder.getInstance().buildReadWriteMultipleRegisters(serverAddress, readAddress, readQuantity, writeAddress, registers);
        ReadWriteMultipleRegistersResponse response = (ReadWriteMultipleRegistersResponse) modbusMaster.processRequest(request);
        return response;
    }

    final public int[] readWriteMultipleRegisters(int serverAddress, int readAddress, int readQuantity, int writeAddress, int[] registers) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException {
        ReadWriteMultipleRegistersResponse response = readWriteMultipleRegistersResponse(serverAddress, readAddress, readQuantity, writeAddress, registers);
        return response.getRegisters();
    }

    final public ReadDiscreteInputsResponse readDiscreteInputsResponse(int slaveId, int offset, int quantity) throws ModbusIOException, ModbusNumberException, ModbusProtocolException {
        open();
        ModbusRequest request = ModbusRequestBuilder.getInstance().buildReadDiscreteInputs(slaveId, offset, quantity);
        ReadDiscreteInputsResponse modbusResponse = (ReadDiscreteInputsResponse) modbusMaster.processRequest(request);
        return modbusResponse;
    }


    final public byte[] readDiscreteInputsToByte(int slaveId, int offset, int quantity) throws ModbusIOException, ModbusProtocolException, ModbusNumberException {
        ReadDiscreteInputsResponse response = readDiscreteInputsResponse(slaveId, offset, quantity);
        return response.getBytes();
    }

    final public Object[] readDiscreteInputsToObject(int slaveId, int offset, int quantity, @NonNull String format) throws ModbusIOException, ModbusProtocolException, ModbusNumberException {
        byte[] data = readDiscreteInputsToByte(slaveId, offset, quantity);
        Object[] values = HoldingRegistersMessage.build().parseHoldingRegistersToObject(format, data);
        return values;
    }

    final public boolean[] readDiscreteInputs(int slaveId, int offset, int quantity) throws ModbusIOException, ModbusProtocolException, ModbusNumberException {
        ReadDiscreteInputsResponse response = readDiscreteInputsResponse(slaveId, offset, quantity);
        return response.getCoils();
    }


}
